# BASE MODEL
# -----------------------------------------------------------------------------
# This serves as the base for all other models.

class BaseModel

    id: null

    # Holds the URL used to load and save data. Loading will use a GET request,
    # and saving will use a POST request.
    url: null

    # Model's data are stored under the `attributes` property.
    attributes:
        creationDate: null     # when was the model created?
        creatorIP: null        # the client IP which created the model
        createdFrom: null      # where was it originated from (dekstop, mobile phone id, email, twitter, etc)

    # The constructor accepts a JSON object with the model's attributes.
    # If the `props` is an array, consider only its first index.
    constructor: (props) ->
        return if not props?

        if props instanceof Array
            @set props[0]
        else
            @set props


    # GENERAL HELPERS
    # -------------------------------------------------------------------------

    # Helper method to get the object's type name.
    getTypeName: =>
        arr = @constructor.toString().match(/function\s*(\w+)/)
        return arr[1]

    # Helper to get the ID of the model to be used as the key
    # on the browser's local storage.
    getLocalId: =>
        return @getTypeName() + "-" + @id

    # Get the URL for saving and loading. Default `db/models_url`.
    getUrl: =>
        return "db/" + @url


    # GET AND SET ATTRIBUTES
    # -------------------------------------------------------------------------

    # Get a property value.
    get: (prop) =>
        return @attributes[prop]

    # Get a property value as boolean.
    getBool: (prop) =>
        result = @get prop
        if result is true or result is 1 or result is "true"
            return true
        else
            return false

    # Set a property value. If `prop` is an object, it will append all its
    # attributes to the current model.
    set: (prop, value, ignoreEmpty) =>
        ignoreEmpty = false if not ignoreEmpty?

        if typeof prop is "string"
            if (not ignoreEmpty) or (ignoreEmpty and value? and value isnt "")
                @attributes[prop] = value
                @id = value if prop is "id"
        else if prop?
            @set p, prop[p], ignoreEmpty for own p of prop

    # Delete the specified property.
    del: (prop) =>
        delete @attributes[prop]

    # SAVE DATA
    # -------------------------------------------------------------------------

    # Save the model. It will first save it on the browser's local storage,
    # then send a PUT request to the server. If the server returns an OK
    # response, delete the saved data from the local storage.
    save: (options) =>
        @saveLocal options
        @saveRemote options

    # Save the model on the browser's local storage.
    saveLocal: (options) =>
        if not window.localStorage?
            return

        window.localStorage.setItem @getLocalId(), @serialize()

    # Send the model to the remote server. If the model's ID is already specified,
    # do a PUT request and send the ID along the attributes, otherwise do a POST.
    saveRemote: (options) =>
        data = @getAttributesForSaving()

        if not @id? or @id is ""
            methodType = "POST"
        else
            methodType = "PUT"
            data.id = @id

        if window.App.Settings.General.debug
            console.log "Model.saveRemote", methodType, data, options

        $.ajax
            error: (xhr, status, err) => @saveRemote_Error options, err
            success: (data) => @saveRemote_Success options, data
            data: data
            dataType: "json"
            timeout: window.App.Settings.Sync.timeout
            type: methodType
            url: @getUrl()

    # When remote saving is NOT successful, trigger the `onError` callback but
    # only if one was specified on the save options.
    saveRemote_Error: (options, err) =>
        if window.App.Settings.General.debug
            console.warn "Model.saveRemote", "Error", err, options

        if options?.onError?
            options.onError this, err

    # When remote saving IS successful, trigger the `onSuccess` callback but
    # only if one was specified on the save options.
    saveRemote_Success: (options, data) =>
        if window.App.Settings.General.debug
            console.log "Model.saveRemote", "Success", data, options

        @id = data.id
        @set data

        if options?.onSuccess?
            options.onSuccess this, data

    # LOAD DATA
    # -------------------------------------------------------------------------

    # Load the model's data and attributes. First check if there's an unsaved
    # model on the browser's local storage, and then fetch the information
    # from the MongoDB server.
    load: (options) =>
        @loadRemote options

    # Load model data from the browser's local storage.
    loadLocal: (options) =>
        if not window.localStorage?
            return

        window.localStorage.getitem @localId()

    # Load model data from the remote MongoDB server.
    loadRemote: (options) =>
        $.ajax
            error: (xhr, status, err) => @loadRemote_Error options, err
            success: (data) => @loadRemote_Success options, data
            dataType: "json"
            timeout: window.App.Settings.Sync.timeout
            type: "GET"
            url: @getUrl()

    # When remote loading is NOT successful, trigger the `onError` callback but
    # only if one was specified on the save options.
    loadRemote_Error: (options, err) =>
        if window.App.Settings.General.debug
            console.log "Model.loadRemote", "Error", err, options

        if options?.onError?
            options.onError this, err

    # When remote loading IS successful, trigger the `onSuccess` callback but
    # only if one was specified on the save options.
    loadRemote_Success: (options, data) =>
        if window.App.Settings.General.debug
            console.log "Model.loadRemote", "Success", data, options

        @id = data.id
        @set data

        if options?.onSuccess?
            options.onSuccess this, data


    # CLEAR DATA
    # -------------------------------------------------------------------------

    # Clear the local storage copy of the model, if there's one.
    clearLocal: =>
        window.localStorage.removeItem @getLocalId()


    # TRANSFORM DATA
    # -------------------------------------------------------------------------

    # Get only valid attributes to save to the local storage or remote database.
    # Valid attributes are the one with values not being undefined, null or empty.
    getAttributesForSaving: =>
        result = {}
        for key, value of @attributes
            result[key] = value if value? and value isnt ""
        return result

    # Serialize the model to a JSON string passing its ID and attributes.
    serialize: =>
        return
        {
            id: @id
            attributes: @getAttributesForSaving()
        }

    # Parse the specified data (string or object) and bind its attributes to the model.
    parse: (data) =>
        @attributes = data


# MODULE EXPORTS
# -----------------------------------------------------------------------------
if exports?
    module.exports = BaseModel
else
    window.App.BaseModel = BaseModel